home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Advanced I⁄O v2.3
/
Advanced i⁄o
/
arithm_coding.cc
next >
Wrap
Text File
|
1995-05-29
|
8KB
|
255 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Arithmetic Coding
*
* The functions implemented here perform actual variable bit decoding/encoding
* of an item (an integer number) using the cumulative probabilities supplied
* by a particular model of the source data.
*
* $Id: arithm_coding.cc,v 2.0 1995/02/07 19:30:52 oleg Exp oleg $
*
************************************************************************
*/
#ifdef __GNUC__
#pragma implementation "arithm.h"
#endif
#include "arithm.h"
/*
*------------------------------------------------------------------------
* Constructors and Destructors
* Note, constructor doesn't yet know if encoding or decoding is to be
* performed. Nor does it open the bit stream. You have to open the
* stream explicitly using the appropriate open() function (see class BitIO and
* EndianIO).
*/
// Just initialize the data fields
ArithmCodingData::ArithmCodingData(Input_Data_Model& model)
: Source_model(model)
{
low = 0; // Start with the full code range
high = Top_value;
bits_to_follow = 0; // Nothing to follow yet
value = 0;
assert( Half == 1<<(Code_size-1) && Third_qtr == Half | First_qtr );
Coding_status = Init;
}
// Initialization
void ArithmCodingOut::initialize(void)
{
Source_model.open((BitOut&)(*this));
Source_model.agree_on_top_freq( (1<<(Code_size-2)) - 1 );
Coding_status = Coding;
}
void ArithmCodingIn::initialize(void)
{
Source_model.open((BitIn&)(*this));
register int i; // Read in the first code value
for(i=0; i<Code_size; i++)
value = (value << 1) | get_bit();;
Source_model.agree_on_top_freq( (1<<(Code_size-2)) - 1 );
Coding_status = Coding;
}
/*
*------------------------------------------------------------------------
* Handling the coded stream
*/
// Close the coded stream
void ArithmCodingOut::close(void)
{
if( Coding_status == EoF )
return; // Already closed
Coding_status = EoF;
encode_index(Source_model.eof_index());
done_encoding();
BitOut::close();
}
void ArithmCodingIn::close(void)
{
Coding_status = EoF;
BitIn::close();
}
// Put a symbol into the coded stream
void ArithmCodingOut::put(const Symbol symbol)
{
Index index = Source_model.get_index(symbol);
encode_index(index);
Source_model.update_model(index);
}
Symbol ArithmCodingIn::get(void)
{
assure( Coding_status != EoF, "Cannot read past End-of-File" );
Index index = decode_index();
if( index == Source_model.eof_index() )
{
Coding_status = EoF;
return 0;
}
// Note, update_model may change
Symbol symbol = Source_model.get_symbol(index);
Source_model.update_model(index); // symbol vs. index mapping, that's why
// get_symbol should precede update_mod
return symbol;
}
/*
*------------------------------------------------------------------------
* Encoding an item
*/
// Output a bit following by
// bits_to_follow opposite bits
inline void ArithmCodingOut::put_bit_plus_follow(const char bit)
{
// message("\nWrite 1 + %d bits",bits_to_follow);
put_bit(bit);
for(; bits_to_follow > 0; bits_to_follow--)
put_bit(!bit); // The loop sets bits_to_follow to 0
}
void ArithmCodingOut::encode_index(const Index index)
{
Code range = high - low + 1; // Current range
// Narrow the code region to that
// allotted to this symbol
// First compute a new low end
low = low + (range * Source_model.cum_freq(index)) /
Source_model.total_cum_freq();
// and add the new range to it
high = low + (range * Source_model.freq(index)) /
Source_model.total_cum_freq() - 1;
if( high <= low )
_error("ArithmCoder went crazy: high <= low!\n"
"index = %d, symbol = %d, freq = %d, cum_freq = %d total = %d\n",
"range = %d (0x%6x), high = 0x%6x, low = 0x%6x",
index, index == Source_model.eof_index() ? 0 :
Source_model.get_symbol(index),
Source_model.freq(index), Source_model.cum_freq(index),
Source_model.total_cum_freq(), range, range, high, low);
#if 0
message("\n\n-----\nindex %d, symbol %d, freq %d, cum_freq %d",
index, index == Source_model.eof_index() ? 0 :
Source_model.get_symbol(index),
Source_model.freq(index), Source_model.cum_freq(index));
message("\nhigh %6x, low %6x", high, low);
#endif
for(;;) // Loop to output bits we're positive about
{
if( !(high & Half) )/*high < Half*/ // If the region is entirely in low
put_bit_plus_follow(0); // half, binary expansion starts w/ 0
else if( low & Half ) // low >= Half, since Top_value = 1<<Codesize-1
{ // Upper half binary numbers begin
put_bit_plus_follow(1); // positively with 1, so output it
low &= ~Half, high &= ~Half; // Subtract the offset to top
}
// if( low <= First_qtr && high < Third_qtr )
else if( low & First_qtr & (high ^ Third_qtr) )
{ // Cannot tell the bit now, but
bits_to_follow++; // the next bit would be opposite
low -= First_qtr, high -= First_qtr; // Subtract offset to middle
}
else
break;
low = low << 1; // Scale up the range, shifting in
high = (high << 1) | 1; // 1 as the LSB for high
}
}
/*
*------------------------------------------------------------------------
* Finish encoding the stream
* Output two bits that select the quarter that contains the current
* code range. Note that the decoder part of the codec analyzes bits when
* they are loaded into the 'value' variable, which acts like
* sort of the buffer. We're analyzing a couple of bits off the top of
* the buffer, shift them out and shift in some bits from the input stream.
* But if the input stream contains only, say, 2 bits that specify the
* termination, we still need to shift them on the top of the buffer.
* I.e. we need to read up to Code_size-2 phony bits after the EOF (that's
* what has been suggested in the 'Text Compression' book).
* However, reading "past" actual EOF makes it impossible to concatenate
* two ArithmCoding streams (or write anything after the encoded stream
* for that matter). Instead of 'reading' phony bits after the EOF,
* we'd rather write some extra bits.
* So, after the encoding is finished, we still need to output enough
* 'phony' bits to prevent EOF on reading. On the other hand, we need
* to make sure that *all* these bits are going be read back on decoding.
*/
void ArithmCodingOut::done_encoding(void)
{
bits_to_follow += 1;
if( low < First_qtr )
put_bit_plus_follow(0);
else
put_bit_plus_follow(1);
register int i; // Output phony bits
for(i=0; i<Code_size-2; i++)
put_bit_plus_follow(0);
}
/*
*------------------------------------------------------------------------
* Decoding an item from the input stream
*/
Index ArithmCodingIn::decode_index()
{
Code range = high - low + 1; // Current code region
// Find the cumulative freq for the
// symbol
Code cum = ((value-low+1)*Source_model.total_cum_freq() - 1) /
range;
Index index = Source_model.find_index(cum);
// Narrow the code region to that
// allotted to this symbol
// First compute a new low end
low = low + (range * Source_model.cum_freq(index)) /
Source_model.total_cum_freq();
// and add the new range to it
high = low + (range * Source_model.freq(index)) /
Source_model.total_cum_freq() - 1;
assert( high > low );
for(;;) // Simulate the encoder and get rid of bits
{ // encoder has put for 'index'
if( !(high & Half) ) // if( high < Half )
; // Expand the low half of code region
else if( low & Half ) // if( low >= Half )
{ // Expand the upper half
value &= ~Half;
low &= ~Half, high &= ~Half;
}
else if( low & First_qtr & (high ^ Third_qtr) )
{ // if( low >= First_qtr && high < Third_qtr )
value -= First_qtr; // Expand middle half
low -= First_qtr, high -= First_qtr;
}
else
break;
low = low << 1; // Scale up the range, shifting in
high = (high << 1) | 1; // 1 as the LSB for high
value = (value << 1) | get_bit();; // Move in the next input bit
}
return index;
}